package com.tsfyun.scm.service.impl.system;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.config.OrikaBeanMapper;
import com.tsfyun.common.base.constant.LoginConstant;
import com.tsfyun.common.base.enums.LoginRoleTypeEnum;
import com.tsfyun.common.base.enums.PersonTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.extension.OrderItem;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.common.base.security.LoginVO;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.scm.dto.system.RoleDTO;
import com.tsfyun.scm.dto.system.RoleSaveDTO;
import com.tsfyun.scm.dto.system.UserBindRoleDTO;
import com.tsfyun.scm.entity.system.SysRole;
import com.tsfyun.scm.entity.user.Person;
import com.tsfyun.scm.mapper.system.SysRoleMapper;
import com.tsfyun.scm.security.config.StringRedisUtils;
import com.tsfyun.scm.service.system.ISysMenuService;
import com.tsfyun.scm.service.system.ISysRoleMenuService;
import com.tsfyun.scm.service.system.ISysRoleService;
import com.tsfyun.scm.service.user.IPersonRoleService;
import com.tsfyun.scm.service.user.IPersonService;
import com.tsfyun.scm.util.TsfWeekendSqls;
import com.tsfyun.scm.vo.system.RolePersonVO;
import com.tsfyun.scm.vo.system.SimpleSysMenuVO;
import com.tsfyun.scm.vo.system.SysRoleInfoVO;
import com.tsfyun.scm.vo.system.SysRoleVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@RefreshScope
@Slf4j
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRole> implements ISysRoleService {

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private OrikaBeanMapper beanMapper;

    @Autowired
    private ISysRoleMenuService sysRoleMenuService;

    @Autowired
    private IPersonRoleService personRoleService;

    @Value("${scm.manage.role.code}")
    private String manageRoleCode;

    @Autowired
    private ISysMenuService sysMenuService;

    @Autowired
    private IPersonService personService;

    @Value("${scm.manage.person.code}")
    private String managePersonCode;

    @Autowired
    protected HttpServletRequest request;

    @Autowired
    private StringRedisUtils stringRedisUtils;

    @Override
    public List<SysRole> getUserRoles(Long personId) {
        return sysRoleMapper.findRolesByPersonId(personId);
    }

    @Override
    public PageInfo<SysRole> pageList(RoleDTO roleDTO) {
        TsfWeekendSqls sqls = TsfWeekendSqls.<SysRole>custom()
                .andLike(true,SysRole::getName,roleDTO.getName())
                .andLike(true,SysRole::getId,roleDTO.getId())
                .andEqualTo(true,SysRole::getDisabled,roleDTO.getDisabled());
        PageInfo<SysRole> pageInfo = super.pageList(roleDTO.getPage(),roleDTO.getLimit(),sqls, Lists.newArrayList(OrderItem.asc("id")));
        return pageInfo;
    }

    @Override
    public List<SysRoleVO> list(RoleDTO roleDTO) {
        SysRole sysRole = new SysRole();
        if(StringUtils.isNotEmpty(roleDTO.getId())) {
            sysRole.setId(roleDTO.getId());
        }
        if(StringUtils.isNotEmpty(roleDTO.getName())) {
            sysRole.setName(roleDTO.getName());
        }
        if(roleDTO.getDisabled()!=null){
            sysRole.setDisabled(roleDTO.getDisabled());
        }
        List<SysRoleVO> roleVOS = new ArrayList<>();
        List<SysRole> roles = super.list(sysRole);
        if(!CollectionUtils.isEmpty(roles)) {
            roleVOS = beanMapper.mapAsList(roles,SysRoleVO.class);
        }
        return roleVOS;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveRole(RoleSaveDTO dto) {
        TsfPreconditions.checkArgument(
                !Objects.equals(dto.getId().toLowerCase(),manageRoleCode)
                && !Arrays.stream(PersonTypeEnum.values()).map(PersonTypeEnum::getCode).map(String::toLowerCase).collect(Collectors.toList()).contains(dto.getId().toLowerCase()),
                new ServiceException("角色编号已被系统占用,请输入其它编号"));
        SysRole sysRole = new SysRole();
        sysRole.setId(dto.getId().toLowerCase());
        if(sysRole.getId().contains(",")) {
            throw new ServiceException("角色编号不能存在特殊字符");
        }
        //角色不允许包含登录角色类型，防止跟权限冲突
        Arrays.asList(LoginRoleTypeEnum.values()).stream().forEach(loginRoleTypeEnum -> {
            if(Objects.equals(loginRoleTypeEnum.getCode().toLowerCase(),sysRole.getId().toLowerCase())) {
                throw new ServiceException("角色编号已被系统占用,请输入其它编号");
            }
        });

        sysRole.setName(dto.getName());
        sysRole.setDisabled(dto.getDisabled());
        sysRole.setLocking(Boolean.FALSE);
        sysRole.setMemo(dto.getMemo());
        try {
            super.saveNonNull(sysRole);
        } catch (DuplicateKeyException e) {
            //主键冲突
            throw new ServiceException("角色编号已经存在");
        }

        //绑定菜单
        if(!CollectionUtils.isEmpty(dto.getMenuIds())) {
            sysRoleMenuService.saveOrUpdate(sysRole.getId(),dto.getMenuIds());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateRole(RoleSaveDTO dto) {
        SysRole sysRole = super.getById(dto.getPreId());
        TsfPreconditions.checkArgument(Objects.nonNull(sysRole),new ServiceException("角色信息不存在"));

        if(!sysRole.getId().equals(dto.getId())){
            TsfPreconditions.checkArgument(!Objects.equals(dto.getId().toLowerCase(),manageRoleCode)
                    && !Arrays.stream(PersonTypeEnum.values()).map(PersonTypeEnum::getCode).map(String::toLowerCase).collect(Collectors.toList()).contains(dto.getId().toLowerCase()),new ServiceException("角色编号已被系统占用,请输入其它编号"));
        }

        TsfPreconditions.checkArgument(Objects.equals(sysRole.getLocking(),Boolean.FALSE),new ServiceException("角色已被锁定禁止修改"));
        sysRole.setId(dto.getId().toLowerCase());
        if(sysRole.getId().contains(",")) {
            throw new ServiceException("角色编号不能存在特殊字符");
        }
        //角色不允许包含登录角色类型，防止跟权限冲突
        Arrays.asList(LoginRoleTypeEnum.values()).stream().forEach(loginRoleTypeEnum -> {
            if(Objects.equals(loginRoleTypeEnum.getCode().toLowerCase(),sysRole.getId().toLowerCase())) {
                throw new ServiceException("角色编号已被系统占用,请输入其它编号");
            }
        });
        sysRole.setName(dto.getName());
        sysRole.setDisabled(dto.getDisabled());
        sysRole.setMemo(dto.getMemo());
        try {
            sysRoleMapper.updateRole(dto, SecurityUtil.getCurrentPersonIdAndName());
        } catch (DuplicateKeyException e) {
            //主键冲突
            throw new ServiceException("角色编号已经存在");
        }
        //绑定菜单
        //可能是清空某角色的菜单权限
        sysRoleMenuService.saveOrUpdate(sysRole.getId(),dto.getMenuIds());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateDisabled(String id, Boolean disabled) {
        SysRole role = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(role),new ServiceException("角色信息不存在"));
        TsfPreconditions.checkArgument(!role.getLocking(),new ServiceException("角色已被锁定禁止修改"));

        SysRole update = new SysRole();
        update.setDisabled(disabled);
        sysRoleMapper.updateByExampleSelective(update, Example.builder(SysRole.class).where(
                WeekendSqls.<SysRole>custom().andEqualTo(SysRole::getId, id)).build()
        );
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeIds(List<String> ids) {
        //只能删除未锁定的角色
        //由于此操作不频繁，改成循环校验操作
        TsfPreconditions.checkArgument(CollectionUtil.isNotEmpty(ids),new ServiceException("请至少选择一条数据"));
        ids.stream().forEach(id->{
            SysRole sysRole = super.getById(id);
            if(Objects.isNull(sysRole)) {
                log.error("数据:{}未找到",id);
                throw new ServiceException("数据未找到");
            }
            TsfPreconditions.checkArgument(Objects.equals(sysRole.getLocking(),Boolean.FALSE),new ServiceException("角色已被锁定禁止删除"));
        });
        //先解除外键关联
        //删除角色与菜单关联
        sysRoleMenuService.deleteBatchByRoleIds(ids);
        //删除角色与员工关联
        personRoleService.deleteBatchByRoleIds(ids);
        super.removeByIds(ids);
    }

    @Override
    public SysRoleInfoVO roleInfo(String id) {
        SysRole sysRole = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(sysRole),new ServiceException("角色信息不存在"));
        SysRoleInfoVO roleInfoVO = beanMapper.map(sysRole,SysRoleInfoVO.class);

        //如果是超级管理员则能查看所有的菜单
        List<SimpleSysMenuVO> menuTrees = sysMenuService.getAllMenusByRole(sysRole);
        roleInfoVO.setMenus(menuTrees);
        return roleInfoVO;
    }

    @Override
    public void userBindRole(UserBindRoleDTO dto) {
        //管理员不允许修改
        Person person = personService.getById(dto.getPersonId());
        TsfPreconditions.checkArgument(Objects.nonNull(person),new ServiceException("员工信息不存在"));
        TsfPreconditions.checkArgument(!Objects.equals(person.getCode(),managePersonCode),new ServiceException("管理员不允许此操作"));
        personRoleService.saveOrUpdate(dto.getPersonId(),dto.getRoleIds());

        //修改redis中的角色数据
        String authPersonKey = LoginConstant.AUTH + dto.getPersonId().toString();
        String authPerson = stringRedisUtils.getToString(authPersonKey);
        if(StringUtils.isNotEmpty(authPerson)) {
            LoginVO loginVO = JSONObject.parseObject(authPerson, LoginVO.class);
            if(CollectionUtil.isEmpty(dto.getRoleIds())) {
                loginVO.setSysRoles(null);
            } else {
                List<SysRole> roleList = Lists.newArrayListWithExpectedSize(dto.getRoleIds().size());
                dto.getRoleIds().stream().forEach(r->{
                    SysRole sysRole = new SysRole();
                    sysRole.setId(r);;
                    sysRole.setName("");//名称暂不赋值
                    roleList.add(sysRole);
                });
                loginVO.setSysRoles(beanMapper.mapAsList(roleList, com.tsfyun.common.base.security.SysRoleVO.class));
            }
            Long authPersonKeyTtl = stringRedisUtils.ttl(authPersonKey);
            if(-2 != authPersonKeyTtl) {
                stringRedisUtils.set(authPersonKey,JSONObject.toJSONString(loginVO),authPersonKeyTtl, TimeUnit.SECONDS);
            }
        }
    }

    @Override
    public List<RolePersonVO> getUserRolesBatch(List<Long> personIds) {
        return sysRoleMapper.findRolesByPersonIdBatch(personIds);
    }
}
